#include <jni.h>
#include <string>

#include "android/log.h"
#include "libmp3lame/lame.h"
#include <stdlib.h>
#include <stdio.h>

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "mp3lame", __VA_ARGS__)

lame_global_flags *gfp;
bool isSTEREO = false;
FILE *fid;

extern "C" JNIEXPORT void JNICALL
Java_com_dream_libmp3lame_MP3Lame_initEnc(JNIEnv *env, jobject,
                                       jint inSampleRate,
                                       jint outSampleRate,
                                       jint inChannels,
                                       jint outChannels,
                                       jint bitRate,
                                       jint quality,
                                       jboolean isVBR) {

    gfp = lame_init();
    lame_set_in_samplerate(gfp, inSampleRate);//原始数据的采样率
    lame_set_out_samplerate(gfp, outSampleRate);//编码后的采样率，如果不设置就和输入的采样率一样
    lame_set_num_channels(gfp, inChannels);//原始数据的通道数

    if (outChannels == 1){
        lame_set_mode(gfp, MONO);//编码后的通道数，STEREO双声道，MONO 单声道，如果不设置就和输入的通道数一样
    }else{
        isSTEREO = true;
        lame_set_mode(gfp, STEREO);//编码后的通道数，STEREO双声道，MONO 单声道，如果不设置就和输入的通道数一样
    }

    if (isVBR){
        lame_set_VBR(gfp, vbr_mtrh);//设置VBR模式,如果不设置默认为CBR,除非资源有限，一般建议使用VBR
        lame_set_VBR_mean_bitrate_kbps(gfp, bitRate);//设置VBR的码率,仅在VBR模式下有效
    }else{
        lame_set_VBR(gfp, vbr_off);//关闭VRB关闭，设置为CBR模式
        lame_set_brate(gfp, bitRate);//设置CBR的码率，仅在CBR模式下有效
    }

    lame_set_quality(gfp, quality);
    int ret = lame_init_params(gfp);

    LOGD("The lame version is: %s,init result is %d", get_lame_version(),ret);
}

extern "C" JNIEXPORT void JNICALL
Java_com_dream_libmp3lame_MP3Lame_initRec(JNIEnv *env, jobject,
                                          jstring filePath,
                                          jint inSampleRate,
                                          jint outSampleRate,
                                          jint inChannels,
                                          jint outChannels,
                                          jint bitRate,
                                          jint quality,
                                          jboolean isVBR) {

    const char *cPath = env->GetStringUTFChars(filePath,0);
    env->ReleaseStringUTFChars(filePath, cPath);
    fid = fopen(cPath, "ab+");

    gfp = lame_init();
    lame_set_in_samplerate(gfp, inSampleRate);//原始数据的采样率
    lame_set_out_samplerate(gfp, outSampleRate);//编码后的采样率，如果不设置就和输入的采样率一样
    lame_set_num_channels(gfp, inChannels);//原始数据的通道数

    if (outChannels == 1){
        lame_set_mode(gfp, MONO);//编码后的通道数，STEREO双声道，MONO 单声道，如果不设置就和输入的通道数一样
    }else{
        isSTEREO = true;
        lame_set_mode(gfp, STEREO);//编码后的通道数，STEREO双声道，MONO 单声道，如果不设置就和输入的通道数一样
    }

    if (isVBR){
        lame_set_VBR(gfp, vbr_mtrh);//设置VBR模式,如果不设置默认为CBR,除非资源有限，一般建议使用VBR
        lame_set_VBR_mean_bitrate_kbps(gfp, bitRate);//设置VBR的码率,仅在VBR模式下有效
    }else{
        lame_set_VBR(gfp, vbr_off);//关闭VRB关闭，设置为CBR模式
        lame_set_brate(gfp, bitRate);//设置CBR的码率，仅在CBR模式下有效
    }

    lame_set_quality(gfp, quality);
    int ret = lame_init_params(gfp);

    LOGD("The lame version is: %s,init result is %d", get_lame_version(),ret);
}


extern "C" jint JNICALL
Java_com_dream_libmp3lame_MP3Lame_encode(JNIEnv *env, jobject obj, jshortArray pcmData,jint pcmLen,jbyteArray mp3ByteArray) {

    short* pcm = env->GetShortArrayElements(pcmData, NULL);
    const int mp3BufferSize = pcmLen * 5 / 4 + 7200;
    unsigned char *mp3Buffer = (unsigned char *)malloc(mp3BufferSize);
    //编码有两个函数，lame_encode_buffer：双声道的数据分别输入，lame_encode_buffer_interleaved：双声道数据交错一起输入，一般使用这个函数，单声道的话，只能用lame_encode_buffer，把单声道数据当成左声道数据传入，右声道传NULL即可
    //编码需要给缓存区，这个缓存区的大小官方建议为：采样率/20+7200。有些博客的最坏情况建议是：1.25*单个通道的采样率+7200

    //返回值如果为负，说明编码失败，返回值为编码后的数据长度

    //编码的函数有两个，lame_encode_buffer：双声道的数据分别输入，lame_encode_buffer_interleaved：双声道数据交错一起输入，一般使用这个函数，单声道的话，只能用lame_encode_buffer，把单声道数据当成左声道数据传入，右声道传NULL即可

    int mp3Length;

    if (isSTEREO){
        //第三个参数是单个通道的数据大小，因为是双声道，所以要除以2
        mp3Length = lame_encode_buffer_interleaved(gfp, pcm, pcmLen / 2, mp3Buffer, mp3BufferSize);
    }else{
        mp3Length = lame_encode_buffer(gfp, pcm, NULL,pcmLen,mp3Buffer, mp3BufferSize);
    }

    if(mp3Length > 0){
        env->SetByteArrayRegion(mp3ByteArray, 0, mp3Length, (jbyte *)mp3Buffer);
    }

    free(mp3Buffer);
    env->ReleaseShortArrayElements(pcmData, pcm, 0);
    return mp3Length;
}

extern "C" void JNICALL
Java_com_dream_libmp3lame_MP3Lame_writeRec(JNIEnv *env, jobject obj, jshortArray pcmData,jint pcmLen) {
//    int pcmLen = env->GetArrayLength(pcmData);
    short* pcm = env->GetShortArrayElements(pcmData, NULL);
    const int mp3BufferSize = pcmLen * 5 / 4 + 7200;
    unsigned char *mp3Buffer = (unsigned char *)malloc(mp3BufferSize);
    //编码有两个函数，lame_encode_buffer：双声道的数据分别输入，lame_encode_buffer_interleaved：双声道数据交错一起输入，一般使用这个函数，单声道的话，只能用lame_encode_buffer，把单声道数据当成左声道数据传入，右声道传NULL即可
    //编码需要给缓存区，这个缓存区的大小官方建议为：采样率/20+7200。有些博客的最坏情况建议是：1.25*单个通道的采样率+7200

    //返回值如果为负，说明编码失败，返回值为编码后的数据长度

    //编码的函数有两个，lame_encode_buffer：双声道的数据分别输入，lame_encode_buffer_interleaved：双声道数据交错一起输入，一般使用这个函数，单声道的话，只能用lame_encode_buffer，把单声道数据当成左声道数据传入，右声道传NULL即可

    int mp3Length;

    if (isSTEREO){
        //第三个参数是单个通道的数据大小，因为是双声道，所以要除以2
        mp3Length = lame_encode_buffer_interleaved(gfp, pcm, pcmLen / 2, mp3Buffer, mp3BufferSize);
    }else{
        mp3Length = lame_encode_buffer(gfp, pcm, NULL,pcmLen,mp3Buffer, mp3BufferSize);
    }

    if(mp3Length > 0){
        fwrite(mp3Buffer, 1, mp3Length, fid);
    }

    free(mp3Buffer);
    env->ReleaseShortArrayElements(pcmData, pcm, 0);
}

extern "C" JNIEXPORT jint JNICALL
Java_com_dream_libmp3lame_MP3Lame_flush(JNIEnv *env, jobject obj,jbyteArray mp3ByteArray) {
    const int bufferSize = 7200;
    unsigned char buffer[bufferSize];
    int mp3Len = lame_encode_flush(gfp, buffer, bufferSize);

    if(mp3ByteArray != nullptr){
        env->SetByteArrayRegion(mp3ByteArray, 0, mp3Len, reinterpret_cast<jbyte*>(buffer));
    }

    return mp3Len;
}

extern "C" JNIEXPORT void JNICALL
Java_com_dream_libmp3lame_MP3Lame_flushRec(JNIEnv *env, jobject obj) {
    const int bufferSize = 7200;
    unsigned char buffer[bufferSize];
    int mp3Len = lame_encode_flush(gfp, buffer, bufferSize);
    if(mp3Len > 0){
        fwrite(buffer, 1, mp3Len, fid);
    }
}

extern "C" JNIEXPORT void JNICALL
Java_com_dream_libmp3lame_MP3Lame_mp3TagsFid(JNIEnv *env, jobject obj) {
    //测试了调不调用都没什么区别...
    lame_mp3_tags_fid(gfp,fid);
}

extern "C" JNIEXPORT void JNICALL
Java_com_dream_libmp3lame_MP3Lame_release(JNIEnv *env, jobject obj) {
    lame_close(gfp);
}